iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0
Mobile Development

花30天做個Android小專案系列 第 20

Day20 - 更新推文及衝突

  • 分享至 

  • xImage
  •  

今天來做更新推文的部分。

更新的部分實作上並沒有太困難的地方,主要是處理衝突比較麻煩。

更新推文

更新的部分我是使用HandlerRunnable來處理,傳送的指令是"qrG"分別代表離開文章、閱讀文章、跳到文章頁尾,接著就是使用Day17的解析推文方法了。

private val updateHandler = Handler(Looper.getMainLooper())
private var isUpdating = false
private var isLoadingMore = false

private val updateRunnable = object : Runnable {
    override fun run() {
        if (isLoadingMore) return
        viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO)
        {
            isUpdating = true
            PttClient.getInstance().send("qrG")
            delay(200L)
            commentList.clear()
            parseComments(PttClient.getInstance().getScreen())
            isUpdating = false
        }

        updateHandler.postDelayed(this, 2000)
    }
}

Runnable的結尾有再次呼叫updateHandler.postDelayed(this, 2000),如此持續更新推文,目前更新間隔是先設2秒(2000毫秒)。

而初次註冊updateRunnable的地方則是在Day17一進入PreviewFragment頁面時解析完推文後。

viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
    PttClient.getInstance().send("G")
    delay(100L)
    parseComments(PttClient.getInstance().getScreen())

    updateHandler.postDelayed(updateRunnable, 2000)
}

最後為了讓更新後的RecyclerView持續在置底顯示最新推文parseComments方法中有加入以下:

private suspend fun parseComments(screen: String) {
    // ...
    val currentPosition =
        (binding.recyclerView.layoutManager as LinearLayoutManager)
            .findLastVisibleItemPosition()
    if (isUpdating && currentPosition == commentList.size - 1) {
        withContext(Dispatchers.Main) {
            binding.recyclerView.scrollToPosition(commentList.size - 1)
        }
    }
}

再來就是要處理自動更新與其他操作的衝突了。

衝突一:滑動畫面與推文更新

這項衝突主要是在當我滑動手機畫面來看上方舊推文時,我不希望因為自動更新又把我拉回置底畫面。
主要的解法就是上方parseComments程式碼區塊的currentPosition == commentList.size - 1條件。

currentPosition的獲取是調用LinearLayoutManagerfindLastVisibleItemPosition,如果當前RecyclerViewposition不在最後一項我就會當作滑動中而停止置底畫面。

衝突二:讀取更多推文與推文更新

Day19的內容中可以看到,在讀取更多推文時我是傳送了^B指令,而更新推文需要傳送qrG,這兩個指令要是一直交錯傳肯定是會有問題的,因此在讀取更多推文時我會將updateRunnableupdateHandler中移除,並且設置isLoadingMore的Flag來避免移除不及。

修改後的moreCommentCallback

adapter.moreCommentCallback = {

    if (!isLoadingMore) {
        val animator = ObjectAnimator.ofFloat(
            binding.resumeUpdate,
            "translationY",
            200f.dpToPx(requireContext()).toFloat(),
            0f
        )
        animator.interpolator = AccelerateDecelerateInterpolator()
        animator.duration = 300
        animator.start()
        isLoadingMore = true
    }

    if (hasMore && !isUpdating) {
        updateHandler.removeCallbacks(updateRunnable)
        viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
            PttClient.getInstance().send(Char(2).toString())
            delay(100L)
            parseComments(PttClient.getInstance().getScreen())
        }
    }
}

isUpdatingtrue我也不會進行讀取更多推文的程式。此外可以看到我有用ObjectAnimator,這個是我在畫面中下加入的一個自動更新按鈕(待會畫面中能看到,就不放layout了)。按鈕的點擊事件就是用來恢復自動更新狀態的。

binding.resumeUpdate.setOnClickListener {
    isLoadingMore = false
    isUpdating = true
    adapter.setData(listOf())
    updateHandler.post(updateRunnable)
    val animator = ObjectAnimator.ofFloat(
        binding.resumeUpdate,
        "translationY",
        0f,
        200f.dpToPx(requireContext()).toFloat()
    )
    animator.interpolator = AccelerateDecelerateInterpolator()
    animator.duration = 300
    animator.start()
}

調用updateHandler.post(updateRunnable)來重新更新推文畫面,同時用adapter.setData(listOf())將目前的內容清掉,避免恢復後沒有自動置底的狀態。

衝突三:離開PreviewFragment

在離開PreviewFragment的過程中需把updateRunnable取消,否則也會有衝突導致畫面卡住或crash。

onBackPressedDispatcher

requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner,
    object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            updateHandler.removeCallbacks(updateRunnable)
            // ...
        }
    })

onDestroyView

override fun onDestroyView() {
    updateHandler.removeCallbacks(updateRunnable)
    super.onDestroyView()
    // ...
}

目前畫面

https://imgur.com/vExOmjH.gif


上一篇
Day19 - 讀取更多推文
下一篇
Day21 - 預覽頁加入按紐
系列文
花30天做個Android小專案30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言